/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.ui.dialogs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Extend;
import org.eclipse.uml2.uml.ExtensionPoint;

import com.cea.papyrus.core.PapyrusPlugin;

/**
 * Displays in a dialog all possible extension locations.
 * 
 * @author Gilles Cannenterre
 * @since 16 nov. 06
 */
public class ChooseSetExtensionLocationDialog extends ChooseSetAssistedDialog implements IChooseDialog {

	/**
	 * The qualified label provider.
	 */
	final private ExtensionPointQualifiedLabelProvider qualifiedLabelProvider = new ExtensionPointQualifiedLabelProvider();
	
	/**
	 * The old extension location.
	 */
	final private EList oldExtensionLocation;
	
	/**
	 * The uml element.
	 */
	final private Element umlElement;
	
	/**
	 * The has changed.
	 */
	private boolean hasChanged = false; 
	
	/**
	 * The Constructor.
	 * 
	 * @param parentShell the parent shell
	 * @param theElement the the element
	 */
	public ChooseSetExtensionLocationDialog(Shell parentShell, Element theElement){
		super(parentShell,"Extended UseCase's Extension Points: ","Extension Location's Extension Points: ");
		umlElement = theElement;
		
		labelProvider = new ExtensionPointLabelProvider();
		decoratedContentProposalProvider = new ExtensionPointContentProposalProvider();
	
		// save old list of extension points (used to compare when ok is pressed)
		oldExtensionLocation = ((Extend)theElement).getExtensionLocations();
		
		Iterator<ExtensionPoint> extensionPoints = oldExtensionLocation.iterator();
		while (extensionPoints.hasNext()){
			selectedElementList.addElement(extensionPoints.next());
		}
		
		// build possible list		
		extensionPoints = getAllExtensionPoints(theElement).iterator();
		while (extensionPoints.hasNext()){
			ExtensionPoint current = (ExtensionPoint)extensionPoints.next();
			if(!selectedElementList.contains(current)) {
				possibleElementList.addElement(current);
			}
		}
	}

	/**
	 * Gets the all extension points.
	 * 
	 * @param theElement the the element
	 * 
	 * @return the all extension points
	 */
	private ArrayList<ExtensionPoint> getAllExtensionPoints(Element theElement) {
		ArrayList<ExtensionPoint> possibleExtensionPoints = new ArrayList<ExtensionPoint>();
		if (((Extend)theElement).getExtendedCase() != null){
			Iterator elements = ((Extend)theElement).getExtendedCase().getMembers().iterator();
			while (elements.hasNext()){
				Element element = (Element) elements.next();
				if (element instanceof ExtensionPoint) {
					possibleExtensionPoints.add((ExtensionPoint)element);
				}
			}
		}
		return possibleExtensionPoints;
	}
	
	/**
	 * Creates the dialog area.
	 * 
	 * @param parent the parent
	 * 
	 * @return the control
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Control composite = super.createDialogArea(parent);
		
		// Add 2 columns to the table area
		// possibleElementsTable.setLinesVisible(true);
		possibleElementsTable.setHeaderVisible(true);

		// 1st column with image/checkboxes - NOTE: The SWT.CENTER has no effect!!
		TableColumn column = new TableColumn(possibleElementsTable, SWT.CENTER, 0);		
		column.setText("Extension Point");
		column.setWidth(150);
		column.addSelectionListener(new SelectionAdapter() {
	       	
			@Override
			public void widgetSelected(SelectionEvent e) {
				possibleElementsTableViewer.setSorter(new AlphabeticalViewerSorter(0));
			}
		});
		
		// 2nd column with task Description
		column = new TableColumn(possibleElementsTable, SWT.LEFT, 1);
		column.setText("Information");
		column.setWidth(165);
		// Add listener to column so tasks are sorted by description when clicked 
		column.addSelectionListener(new SelectionAdapter() {
       	
			@Override
			public void widgetSelected(SelectionEvent e) {
				possibleElementsTableViewer.setSorter(new AlphabeticalViewerSorter(1));
			}
		});

		// set sorter to the possible element table viewer 
		possibleElementsTableViewer.setSorter(new AlphabeticalViewerSorter(0));
		
		return composite;
	}

	/**
	 * Run add element.
	 * 
	 * @param name the name
	 */
	@Override
	protected void runAddElement(String name) {
		// find the extension point in the list
		ExtensionPoint extensionPoint =null;
		Iterator<ExtensionPoint> it = possibleElementList.getElements().iterator();
		while (it.hasNext()) {
			ExtensionPoint element = (ExtensionPoint) it.next();
			if( name.equalsIgnoreCase(element.getName()) || name.equalsIgnoreCase(element.getQualifiedName())) {
				extensionPoint = element;
			}
		}
		if(extensionPoint != null) {
			runActionAdd(extensionPoint);
		}
	}

	/**
	 * Checks if is selectable element.
	 * 
	 * @param text the text
	 * 
	 * @return true, if is selectable element
	 */
	@Override
	protected boolean isSelectableElement(String text) {
		// iterate through all possibilities and return true if text corresponds
		Iterator<ExtensionPoint> it = possibleElementList.getElements().iterator();
		while (it.hasNext()) {
			ExtensionPoint element = (ExtensionPoint) it.next();
			if( text.equalsIgnoreCase(element.getName()) || text.equalsIgnoreCase(element.getQualifiedName())) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Ok pressed.
	 */
	@Override
	protected void okPressed() {
		int modifCount = 0;
		
		// apply new extension point (diff old and new List)
		modifCount = createExtensionPoints();
		
		// removed unused extension points
		modifCount += destroyExtensionPoints();
		
		// order the list of select extension points
		modifCount += orderExtensionPoints();
		
		// element has changed if there is a modification or more
		hasChanged = (modifCount > 0);
		
		super.okPressed();
	}

	/**
	 * Creates the extension points.
	 * 
	 * @return the int
	 */
	private int createExtensionPoints() {
		int modifCount=0;	// count number of creations (to force refresh as dialog is closed)
		// for all element of the new List that is not in the old list, add the extension point in the extension location list
		Iterator newElements = selectedElementList.getElements().iterator();
		while(newElements.hasNext()) {
			ExtensionPoint extensionPoint = (ExtensionPoint) newElements.next();
			if(!oldExtensionLocation.contains(extensionPoint)) {
				oldExtensionLocation.add(extensionPoint);
				modifCount++;
			}
		}
		return modifCount;
	}
	
	/**
	 * Destroy extension points.
	 * 
	 * @return the int
	 */
	private int destroyExtensionPoints() {
		int modifCount=0;	// count number of destructions (to force refresh as dialog is closed)
		// for all element of the old List that is not in the new list, remove the extension 
		// point from the extension location list
		ArrayList<ExtensionPoint> elementsToDestroy= new ArrayList<ExtensionPoint>();
		Iterator oldElements = oldExtensionLocation.iterator();
		while(oldElements.hasNext()) {
			ExtensionPoint extensionPoint = (ExtensionPoint) oldElements.next();
			if(!selectedElementList.getElements().contains(extensionPoint)) {
				elementsToDestroy.add(extensionPoint);
				modifCount++;
			}
		}	
		// apply modifications
		Iterator elementToDestroy = elementsToDestroy.iterator();
		while(elementToDestroy.hasNext()) {
			ExtensionPoint extensionPoint = (ExtensionPoint) elementToDestroy.next();
			oldExtensionLocation.remove(extensionPoint);
		}
		return modifCount;
	}

	/**
	 * Order extension points.
	 * 
	 * @return the int
	 */
	private int orderExtensionPoints() {
		int modifCount=0;	// count number of destructions (to force refresh as dialog is closed)	
		// for all element of the old List that is not in the new list, remove the extension 
		// point from the extension location list
		Map<ExtensionPoint, Integer> elementsToMove = new HashMap<ExtensionPoint, Integer>();
		Iterator it = oldExtensionLocation.iterator();
		while(it.hasNext()) {
			// position of the extension point in the new list
			ExtensionPoint extensionPoint = (ExtensionPoint) it.next();
			int newIndex = selectedElementList.getElements().indexOf(extensionPoint);
			if(oldExtensionLocation.indexOf(extensionPoint) != newIndex) {
				// extension point should be moved
				elementsToMove.put(extensionPoint, new Integer(newIndex));
				modifCount++;
			}
		}
		// apply modifications
		Iterator elementToMove = elementsToMove.keySet().iterator();
		while(elementToMove.hasNext()) {
			ExtensionPoint extensionPoint = (ExtensionPoint) elementToMove.next();
			oldExtensionLocation.move(((Integer)(elementsToMove.get(extensionPoint))).intValue(), extensionPoint);;
		}
		return modifCount;
	}


	/**
	 * The Class ExtensionPointContentProposalProvider.
	 */
	public class ExtensionPointContentProposalProvider extends DecoratedContentProposalProvider {

		/* (non-Javadoc)
		 * @see com.cea.papyrus.core.ui.dialogs.ChooseSetAssistedDialog.DecoratedContentProposalProvider#getProposals(java.lang.String, int)
		 */
		/**
		 * Gets the proposals.
		 * 
		 * @param contents the contents
		 * @param position the position
		 * 
		 * @return the proposals
		 */
		@Override
		public DecoratedContentProposal[] getProposals(String contents, int position) {
			ArrayList<DecoratedContentProposal> proposals = new ArrayList<DecoratedContentProposal>();

			if(possibleElementList != null) {
				Iterator it = possibleElementList.getElements().iterator();
				while(it.hasNext()) {
					final ExtensionPoint extensionPoint = (ExtensionPoint)it.next();
					final String simpleName = extensionPoint.getName();
					final String qualifiedName = extensionPoint.getQualifiedName();

					if( position < simpleName.length() && contents.substring(0, position).equalsIgnoreCase(simpleName.substring(0, position))) {
						proposals.add(new DecoratedContentProposal(extensionPoint, labelProvider));
					}

					if( position < qualifiedName.length() && contents.substring(0, position).equalsIgnoreCase(qualifiedName.substring(0, position))) {
						proposals.add(new DecoratedContentProposal(extensionPoint, qualifiedLabelProvider));
					}
				}
			}
			
			Collections.sort(proposals);
			return proposals.toArray(new DecoratedContentProposal[proposals.size()]);
		}
	}

	/**
	 * The Class ExtensionPointLabelProvider.
	 */
	protected class ExtensionPointLabelProvider extends LabelProvider implements ITableLabelProvider {

		/**
		 * Gets the image.
		 * 
		 * @param element the element
		 * 
		 * @return the image
		 */
		@Override
		public Image getImage(Object element) {
			Image image = null;
			if((element instanceof ExtensionPoint)) {
				image = PapyrusPlugin.getIconElement((ExtensionPoint)element);
			}
			// return the image if not null or the default stereotype image if stereotype has no image
			return (image != null) ? image : PapyrusPlugin.UML_LABEL_PROVIDER.getImage(ExtensionPoint.class, null) ;
		}

		/**
		 * Gets the text.
		 * 
		 * @param extensionPoint the extension point
		 * 
		 * @return the text
		 */
		@Override
		public String getText(Object extensionPoint) {
			if(!(extensionPoint instanceof ExtensionPoint)) {
				return "not an extension point";
			} else {
				return ((ExtensionPoint)extensionPoint).getName();
			}
		}

		/**
		 * Gets the column image.
		 * 
		 * @param element the element
		 * @param columnIndex the column index
		 * 
		 * @return the column image
		 */
		public Image getColumnImage(Object element, int columnIndex) {
			if(columnIndex == 0) {
				return getImage(element);
			}
			return null;
		}

		/**
		 * Gets the column text.
		 * 
		 * @param element the element
		 * @param columnIndex the column index
		 * 
		 * @return the column text
		 */
		public String getColumnText(Object element, int columnIndex) {
			if(columnIndex == 0) {
				return getText(element);
			} else if(columnIndex == 1) {
				return ((ExtensionPoint)element).getQualifiedName();
			}
			return "not the right column index";
		}
	}

	/**
	 * The Class ExtensionPointQualifiedLabelProvider.
	 */
	protected class ExtensionPointQualifiedLabelProvider extends ExtensionPointLabelProvider {
		
		/**
		 * Gets the text.
		 * 
		 * @param extensionPoint the extension point
		 * 
		 * @return the text
		 */
		@Override
		public String getText(Object extensionPoint) {
			if(!(extensionPoint instanceof ExtensionPoint)) {
				return "not a extension point";
			} else {
				return ((ExtensionPoint)extensionPoint).getQualifiedName();
			}
		}
	}

	/**
	 * Checks for element changed.
	 * 
	 * @return true, if has element changed
	 */
	public boolean hasElementChanged() {
		return hasChanged;
	}	
}